package at.tugraz.hidipic.web;

import at.tugraz.hidipic.model.*;
import at.tugraz.hidipic.service.DataAccessService;
import at.tugraz.hidipic.web.bean.UserFormBean;
import at.tugraz.hidipic.web.propertyeditor.ProjectPropertyEditor;
import at.tugraz.hidipic.web.propertyeditor.UserPropertyEditor;
import at.tugraz.hidipic.web.validation.UserFormBeanValidator;
import java.util.Date;
import java.util.List;
import java.util.Set;
import javax.validation.Valid;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomCollectionEditor;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping(value = "/admin")
public class AdminController {

    private final Logger LOG = Logger.getLogger(AdminController.class);
    @Autowired
    private HibernateTemplate hibernateTemplate;
    @Autowired
    DataAccessService dataAccessService;
    @Autowired
    UserFormBeanValidator userFormBeanValidator;

    @InitBinder()
    protected void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(Set.class, "authorities", new CustomCollectionEditor(Set.class) {

            @Override
            protected Object convertElement(Object element) {
                if (element instanceof String) {
                    CustomGrantedAuthority cga = new CustomGrantedAuthority();
                    String elem = (String) element;
                    cga.setAuthority(elem);
                    return cga;
                }
                return element;
            }
        });
        binder.registerCustomEditor(Set.class, "methods", new CustomCollectionEditor(Set.class) {

            @Override
            protected Object convertElement(Object element) {
                if (element instanceof String) {
                    Method method = new Method();
                    String elem = (String) element;
                    method.setName(elem);
                    return method;
                }
                return element;
            }
        });
        binder.registerCustomEditor(Set.class, "researchers", new CustomCollectionEditor(Set.class) {

            @Override
            protected Object convertElement(Object element) {
                if (element instanceof String) {
                    User user = new User();
                    String elem = (String) element;
                    user.setUsername(elem);
                    return user;
                }
                return element;
            }
        });
        binder.registerCustomEditor(User.class, "projectLeader", new UserPropertyEditor());
        binder.registerCustomEditor(Project.class, "parent", new ProjectPropertyEditor());
    }

    /**
     * default-handler for the admin page, user admin page
     * @return model and view for the user-admin page
     */
    @RequestMapping(value = "/*", method = RequestMethod.GET)
    public ModelAndView handleRequest() {
        ModelAndView mav = new ModelAndView("admin/home");
        List<User> userList = hibernateTemplate.find("from User where username != ?", KDDConstants.ADMIN_NAME);
        mav.addObject("userList", userList);
        return mav;
    }

    /**
     * request for showing the method-administration
     * @return the model and view
     */
    @RequestMapping(value = "/method", method = RequestMethod.GET)
    public ModelAndView showMethods() {
        ModelAndView mav = new ModelAndView("admin/method");
        List<Method> methods = hibernateTemplate.find("from Method");
        mav.addObject("methods", methods);
        return mav;
    }

    /**
     * request for deleting a method
     * @param method
     * @return redirect to the method-administration
     */
    @RequestMapping(value = "/method/delete/{method}", method = RequestMethod.GET)
    public ModelAndView deleteMethod(@PathVariable("method") String method) {
//        Method meth = (Method) hibernateTemplate.get(Method.class, method);
//        String errorString = null;
//        String successString = null;
//        if (meth != null) {
//            try {
//                hibernateTemplate.delete(meth);
//                successString = "error.successmessage";
//            } catch (Exception e) {
//                errorString = "error.defaultmessage";
//            }
//        } else {
//            errorString = "error.defaultmessage";
//        }
        ModelAndView mav = showMethods();
//        setErrorSuccessStrings(errorString, successString, mav);
        return mav;
    }

    /**
     * request for showing the form to add a method
     * @return the model and view
     */
    @RequestMapping(value = "/method/add", method = RequestMethod.GET)
    private ModelAndView addMethod() {
        ModelAndView mav = new ModelAndView("admin/method/add");
        Method method = new Method();
        mav.addObject("method", method);
        return mav;
    }

    /**
     * request for adding a method
     * @param method
     * @param result
     * @return redirect to method administration
     */
    @RequestMapping(value = "/method/add", method = RequestMethod.POST)
    private ModelAndView doMethod(@ModelAttribute("method") @Valid Method method, BindingResult result) {
        String errorString = null;
        String successString = null;
        if (!result.hasErrors()) {
            try {
                hibernateTemplate.save(method);
                successString = "error.successmessage";
                ModelAndView mav = showMethods();
                setErrorSuccessStrings(errorString, successString, mav);
                return mav;
            } catch (Exception e) {
                LOG.error(e);
            }
        } else {
            ModelAndView mav = addMethod();
            mav.addObject("method", method);
            return mav;
        }
        errorString = getErrorsString(result.getAllErrors());
        ModelAndView mav = showMethods();
        setErrorSuccessStrings(errorString, successString, mav);
        return mav;
    }

    /**
     * request for deleting a user
     * @param user
     * @return redirect to user-management
     */
    @RequestMapping(value = "/user/delete/{user}", method = RequestMethod.GET)
    public ModelAndView deleteUser(@PathVariable("user") String user) {
//        User selectedUser = (User) hibernateTemplate.get(User.class, user);
//        String errorString = null;
//        String successString = null;
//        if (selectedUser != null) {
//            try {
//                hibernateTemplate.delete(selectedUser);
//                successString = "error.successmessage";
//            } catch (Exception e) {
//                errorString = "error.defaultmessage";
//            }
//        } else {
//            errorString = "error.defaultmessage";
//        }
        ModelAndView mav = handleRequest();
//        setErrorSuccessStrings(errorString, successString, mav);
        return mav;
    }

    /**
     * request for showing the edit-form for a user
     * @param user
     * @return the model and view of the form
     */
    @RequestMapping(value = "/user/edit/{user}", method = RequestMethod.GET)
    public ModelAndView editUser(@PathVariable("user") String user) {
        ModelAndView mav = new ModelAndView("admin/user/edit");
        User selectedUser = (User) hibernateTemplate.get(User.class, user);
        String errorString = null;
        String successString = null;
        if (selectedUser != null) {
            mav.addObject("editUser", selectedUser);
            UserFormBean ufb = new UserFormBean();
            List<CustomGrantedAuthority> auths = hibernateTemplate.find("from CustomGrantedAuthority");
            Set<CustomGrantedAuthority> authsUser = (Set<CustomGrantedAuthority>) selectedUser.getAuthorities();
            mav.addObject("authList", auths);
            ufb.setAuthorities(authsUser);
            ufb.setUsername(selectedUser.getUsername());
            mav.addObject("editUserBean", ufb);
        } else {
            mav = handleRequest();
            errorString = "error.defaultmessage";
        }
        setErrorSuccessStrings(errorString, successString, mav);
        return mav;
    }

    /**
     * request for showing the form for adding a user
     * @return the model and view of the form
     */
    @RequestMapping(value = "/user/add", method = RequestMethod.GET)
    private ModelAndView addUser() {
        ModelAndView mav = new ModelAndView("admin/user/add");
        UserFormBean ufb = new UserFormBean();
        List<CustomGrantedAuthority> auths = hibernateTemplate.find("from CustomGrantedAuthority");
        mav.addObject("authList", auths);
        mav.addObject("addUserBean", ufb);
        return mav;
    }

    /**
     * request for adding a user
     * @param addUserBean
     * @param result
     * @return redirect to the user-management
     */
    @RequestMapping(value = "/user/add", method = RequestMethod.POST)
    private ModelAndView doAddUser(@ModelAttribute("addUserBean") UserFormBean addUserBean, BindingResult result) {
        String errorString = null;
        String successString = null;
        if (addUserBean != null) {
            String userName = addUserBean.getUsername();
            String pw = addUserBean.getPassword();
            Set<CustomGrantedAuthority> auths = addUserBean.getAuthorities();
            userFormBeanValidator.validate(addUserBean, result, false);
            if (!result.hasErrors()) {
                try {
                    User newUser = new User();
                    newUser.setEnabled(true);
                    newUser.setAccountNonLocked(true);
                    newUser.setUsername(userName);
                    Md5PasswordEncoder md5pe = new Md5PasswordEncoder();
                    newUser.setPassword(md5pe.encodePassword(pw, null));
                    newUser.setAuthorities(dataAccessService.handleAuthorities(auths));
                    hibernateTemplate.save(newUser);
                    ModelAndView mav = handleRequest();
                    successString = "error.successmessage";
                    setErrorSuccessStrings(errorString, successString, mav);
                    return mav;
                } catch (Exception e) {
                    LOG.error(e);
                }
            }
        }
        if (result.hasErrors()) {
            ModelAndView mav = addUser();
            mav.addObject("addUserBean", addUserBean);
            return mav;
        } else {
            errorString = "error.defaultmessage";
        }
        ModelAndView mav = handleRequest();
        setErrorSuccessStrings(errorString, successString, mav);
        return mav;
    }

    /**
     * request for editing a user
     * @param user
     * @param editUserBean
     * @param result
     * @return redirect to user-management
     */
    @RequestMapping(value = "/user/edit/{user}", method = RequestMethod.POST)
    public ModelAndView doEditUser(@PathVariable("user") String user, @ModelAttribute("editUserBean") UserFormBean editUserBean, BindingResult result) {
        String errorString = null;
        String successString = null;
        User selectedUser = (User) hibernateTemplate.get(User.class, user);
        if (editUserBean != null) {
            String pw = editUserBean.getPassword();
            Set<CustomGrantedAuthority> auths = editUserBean.getAuthorities();
            if (selectedUser != null) {
                editUserBean.setUsername(user);
                userFormBeanValidator.validate(editUserBean, result, true);
                if (!result.hasErrors()) {
                    try {
                        selectedUser.setAuthorities(dataAccessService.handleAuthorities(auths));
                        if (pw != null && StringUtils.isNotEmpty(pw)) {
                            Md5PasswordEncoder md5pe = new Md5PasswordEncoder();
                            selectedUser.setPassword(md5pe.encodePassword(pw, null));
                        }
                        selectedUser.setEnabled(true);
                        selectedUser.setAccountNonLocked(true);
                        hibernateTemplate.merge(selectedUser);
                        ModelAndView mav = handleRequest();
                        successString = "error.successmessage";
                        setErrorSuccessStrings(errorString, successString, mav);
                        return mav;
                    } catch (Exception e) {
                        LOG.error(e);
                    }
                }
            }
        }
        if (result.hasErrors()) {
            ModelAndView mav = editUser(user);
            mav.addObject("editUserBean", editUserBean);
            return mav;
        } else {
            errorString = "error.defaultmessage";
        }
        ModelAndView mav = handleRequest();
        setErrorSuccessStrings(errorString, successString, mav);
        return mav;
    }

    private String getErrorsString(List<ObjectError> errors) {
        if (errors != null) {
            StringBuilder sb = new StringBuilder();
            sb.append("error.defaultmessage<br />");
            for (ObjectError o : errors) {
                sb.append(o.getDefaultMessage());
                sb.append("<br />");
            }
            return sb.toString();
        }
        return null;
    }

    /**
     * sets error and success-strings for the actions
     * @param error
     * @param success
     * @param mav
     * @return the model and view with the added errors
     */
    private ModelAndView setErrorSuccessStrings(String error, String success, ModelAndView mav) {
        if (error != null) {
            success = null;
            mav.addObject("errorString", error);
        }
        if (success != null) {
            mav.addObject("successString", success);
        }
        return mav;
    }

    /**
     * request for the project management
     * @return the model and view
     */
    @RequestMapping(value = "/project", method = RequestMethod.GET)
    public ModelAndView showProjects() {
        ModelAndView mav = new ModelAndView("admin/project");
        List<User> projectList = hibernateTemplate.find("from Project");
        mav.addObject("projectList", projectList);
        return mav;
    }

    /**
     * request for deleting a project
     * @param project
     * @return redirect to the project management
     */
    @RequestMapping(value = "/project/delete/{project}", method = RequestMethod.GET)
    public ModelAndView deleteProject(@PathVariable("project") String project) {
//        String errorString = null;
//        String successString = null;
//        int projectId = -1;
//        try {
//            projectId = Integer.parseInt(project);
//        } catch (Exception e) {
//            LOG.error(e);
//            errorString = "error.defaultmessage";
//        }
//        if (projectId >= 0) {
//            Project selectedProject = (Project) hibernateTemplate.get(Project.class, projectId);
//            if (selectedProject != null) {
//                String projectName = selectedProject.getName();
//                try {
//                    hibernateTemplate.delete(selectedProject);
//                    successString = "error.successmessage";
//                } catch (Exception e) {
//                    LOG.error(e);
//                }
//            } else {
//                errorString = "error.defaultmessage";
//            }
//        }
        ModelAndView mav = showProjects();
//        setErrorSuccessStrings(errorString, successString, mav);
        return mav;
    }

    /**
     * request for showing the form for adding a project
     * @return model and view of the form
     */
    @RequestMapping(value = "/project/add", method = RequestMethod.GET)
    private ModelAndView addProject() {
        ModelAndView mav = new ModelAndView("admin/project/add");
        Project project = new Project();
        List<Method> methods = hibernateTemplate.find("from Method");
        List<User> researchers = dataAccessService.getAllUsersWithLevel(KDDConstants.RESEARCHER_ROLE);
        mav.addObject("researcherList", researchers);
        mav.addObject("methodList", methods);
        mav.addObject("project", project);
        return mav;
    }

    /**
     * request for adding a project
     * @param project
     * @param result
     * @return redirect to project-management
     */
    @RequestMapping(value = "/project/add", method = RequestMethod.POST)
    private ModelAndView doAddProject(@ModelAttribute("project") @Valid Project project, BindingResult result) {
        String errorString = null;
        String successString = null;
        if (!result.hasErrors()) {
            try {
                project.setCreationDate(new Date());
                User u = hibernateTemplate.get(User.class, ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername());
                project.setCreator(u);
                hibernateTemplate.save(project);
                successString = "error.successmessage";
                ModelAndView mav = showProjects();
                setErrorSuccessStrings(errorString, successString, mav);
                return mav;
            } catch (Exception e) {
                LOG.error(e);
            }
        } else {
            ModelAndView mav = addProject();
            mav.addObject("project", project);
            return mav;
        }
        errorString = getErrorsString(result.getAllErrors());
        ModelAndView mav = showProjects();
        setErrorSuccessStrings(errorString, successString, mav);
        return mav;
    }

    /**
     * request for showing a form to edit a project
     * @param project
     * @return the model and view of the form
     */
    @RequestMapping(value = "/project/edit/{project}", method = RequestMethod.GET)
    private ModelAndView editProject(@PathVariable("project") String project) {
        String errorString = null;
        String successString = null;
        ModelAndView mav = new ModelAndView("admin/project/edit");
        int projectId = -1;
        try {
            projectId = Integer.parseInt(project);
        } catch (Exception e) {
            LOG.error(e);
        }
        if (projectId >= 0) {
            Project selectedProject = (Project) hibernateTemplate.get(Project.class, projectId);
            List<Method> methods = hibernateTemplate.find("from Method");
            List<User> researchers = dataAccessService.getAllUsersWithLevel(KDDConstants.RESEARCHER_ROLE);
            mav.addObject("researcherList", researchers);
            mav.addObject("methodList", methods);
            mav.addObject("project", selectedProject);
            return mav;
        }
        errorString = "error.defaultmessage";
        mav = showProjects();
        setErrorSuccessStrings(errorString, successString, mav);
        return mav;
    }

    /**
     * request for editing a project
     * @param projectId
     * @param project
     * @param result
     * @return redirect to the project management
     */
    @RequestMapping(value = "/project/edit/{projectid}", method = RequestMethod.POST)
    private ModelAndView doEditProject(@PathVariable("projectid") String projectId, @ModelAttribute("project") @Valid Project project, BindingResult result) {
        String errorString = null;
        String successString = null;
        int projId = -1;
        try {
            projId = Integer.parseInt(projectId);
        } catch (Exception e) {
            LOG.error(e);
        }
        if (projId >= 0) {
            project.setId(projId);
            if (!result.hasErrors()) {
                try {
                    Project updateProject = hibernateTemplate.get(Project.class, projId);
                    dataAccessService.updateProjectFromEdit(project, updateProject);
                    hibernateTemplate.merge(project);
                    ModelAndView mav = showProjects();
                    successString = "error.successmessage";
                    setErrorSuccessStrings(errorString, successString, mav);
                    return mav;
                } catch (Exception e) {
                    LOG.error(e);
                }
            } else {
                ModelAndView mav = editProject(projectId);
                mav.addObject("project", project);
                return mav;
            }
        }
        errorString = "error.defaultmessage";
        ModelAndView mav = showProjects();
        setErrorSuccessStrings(errorString, successString, mav);
        return mav;
    }

    /**
     * request for subproject-management
     * @return the model and view
     */
    @RequestMapping(value = "/subproject", method = RequestMethod.GET)
    public ModelAndView showSubProjects() {
        ModelAndView mav = new ModelAndView("/admin/subproject");
        List<Project> projects = hibernateTemplate.find("from Project");
        for (Project p : projects) {
            p.setSubProjects(dataAccessService.getSubProjectsForProject(p));
        }
        mav.addObject("projectList", projects);
        return mav;
    }

    /**
     * request for deleting a subproject
     * @param subProjectId
     * @return redirect to subproject management
     */
    @RequestMapping(value = "/subproject/delete/{subProjectId}", method = RequestMethod.GET)
    public ModelAndView deleteSubProject(@PathVariable("subProjectId") String subProjectId) {
//        String errorString = null;
//        String successString = null;
//        int projectId = -1;
//        try {
//            projectId = Integer.parseInt(subProjectId);
//        } catch (Exception e) {
//            LOG.error(e);
//            errorString = "error.defaultmessage";
//        }
//        if (projectId >= 0) {
//            SubProject selectedProject = (SubProject) hibernateTemplate.get(SubProject.class, projectId);
//            if (selectedProject != null) {
//                try {
//                    hibernateTemplate.delete(selectedProject);
//                    successString = "error.successmessage";
//                } catch (Exception e) {
//                    LOG.error(e);
//                }
//            } else {
//                errorString = "error.defaultmessage";
//            }
//        }
        ModelAndView mav = showSubProjects();
//        setErrorSuccessStrings(errorString, successString, mav);
        return mav;
    }

    /**
     * request for showing a form to add a subproject
     * @return model and view of the form
     */
    @RequestMapping(value = "/subproject/add", method = RequestMethod.GET)
    public ModelAndView addSubProject() {
        ModelAndView mav = new ModelAndView("/admin/subproject/add");
        SubProject subProject = new SubProject();
        List<User> researchers = dataAccessService.getAllUsersWithLevel(KDDConstants.RESEARCHER_ROLE);
        List<Project> projects = hibernateTemplate.find("from Project");
        mav.addObject("researcherList", researchers);
        mav.addObject("projects", projects);
        mav.addObject("subProject", subProject);
        return mav;
    }

    /**
     * request for adding a subproject
     * @param subProject
     * @param result
     * @return redirect to the subproject-management
     */
    @RequestMapping(value = "/subproject/add", method = RequestMethod.POST)
    private ModelAndView doAddSubProject(@ModelAttribute("subProject") @Valid SubProject subProject, BindingResult result) {
        String errorString = null;
        String successString = null;
        if (!result.hasErrors()) {
            try {
                subProject.setCreationDate(new Date());
                User u = hibernateTemplate.get(User.class, ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername());
                subProject.setCreator(u);
                hibernateTemplate.save(subProject);
                successString = "error.successmessage";
                ModelAndView mav = showSubProjects();
                setErrorSuccessStrings(errorString, successString, mav);
                return mav;
            } catch (Exception e) {
                LOG.error(e);
            }
        } else {
            ModelAndView mav = addSubProject();
            mav.addObject("subProject", subProject);
            return mav;
        }
        errorString = getErrorsString(result.getAllErrors());
        ModelAndView mav = showSubProjects();
        setErrorSuccessStrings(errorString, successString, mav);
        return mav;
    }

    /**
     * request for showing the form to edit a subproject
     * @param subProjectId
     * @return the model and view of the form
     */
    @RequestMapping(value = "/subproject/edit/{subProjectId}", method = RequestMethod.GET)
    private ModelAndView editSubProject(@PathVariable("subProjectId") String subProjectId) {
        String errorString = null;
        String successString = null;
        ModelAndView mav = new ModelAndView("admin/subproject/edit");
        int projectId = -1;
        try {
            projectId = Integer.parseInt(subProjectId);
        } catch (Exception e) {
            LOG.error(e);
        }
        if (projectId >= 0) {
            SubProject subProject = (SubProject) hibernateTemplate.get(SubProject.class, projectId);
            List<User> researchers = dataAccessService.getAllUsersWithLevel(KDDConstants.RESEARCHER_ROLE);
            List<Project> projects = hibernateTemplate.find("from Project");
            mav.addObject("researcherList", researchers);
            mav.addObject("projects", projects);
            mav.addObject("subProject", subProject);
            return mav;
        }
        errorString = "error.defaultmessage";
        mav = showSubProjects();
        setErrorSuccessStrings(errorString, successString, mav);
        return mav;
    }

    /**
     * request for editing a subproject
     * @param projectId
     * @param subProject
     * @param result
     * @return redirect to subproject-management
     */
    @RequestMapping(value = "/subproject/edit/{subProjectId}", method = RequestMethod.POST)
    private ModelAndView doEditProject(@PathVariable("subProjectId") String projectId, @ModelAttribute("subProject") @Valid SubProject subProject, BindingResult result) {
        String errorString = null;
        String successString = null;
        int projId = -1;
        try {
            projId = Integer.parseInt(projectId);
        } catch (Exception e) {
            LOG.error(e);
        }
        if (projId >= 0) {
            subProject.setId(projId);
            if (!result.hasErrors()) {
                try {
                    SubProject updateProject = hibernateTemplate.get(SubProject.class, projId);
                    dataAccessService.updateSubProjectFromEdit(subProject, updateProject);
                    hibernateTemplate.merge(subProject);
                    ModelAndView mav = showSubProjects();
                    successString = "error.successmessage";
                    setErrorSuccessStrings(errorString, successString, mav);
                    return mav;
                } catch (Exception e) {
                    LOG.error(e);
                }
            } else {
                ModelAndView mav = editSubProject(projectId);
                mav.addObject("subProject", subProject);
                return mav;
            }
        }
        errorString = "error.defaultmessage";
        ModelAndView mav = showSubProjects();
        setErrorSuccessStrings(errorString, successString, mav);
        return mav;
    }
}
